iT邦幫忙

2026 iThome 鐵人賽

DAY 2
0

前言

在上一篇文章中,我們認識了FHIR RESTful API的read互動。

如果已經知道Patient的Resource id,就可以用以下形式讀取資料:

GET [base]/Patient/[id]

例如:

GET https://hospital.example.org/fhir/Patient/patient-001

但是,現實情況中不一定會事先知道FHIR Server替Patient分配的id。

使用者可能只知道:

  • 病人姓名
  • 出生日期
  • 性別
  • 病歷號
  • 電話
  • 地址
  • 管理病歷的醫療機構

這時就需要使用FHIR的search互動,透過搜尋參數找出可能符合條件的Patient。

今天不進行實際API操作,而是透過Request及Response範例,認識FHIR Patient搜尋的基本概念。

本文使用的姓名、病歷號、電話及網址均為虛構教學資料。


FHIR搜尋的基本格式

FHIR搜尋的基本形式是:

GET [base]/[Resource Type]?[parameter]=[value]

如果要搜尋Patient:

GET [base]/Patient?[parameter]=[value]

例如,按照姓名搜尋:

GET https://hospital.example.org/fhir/Patient?name=王小明

可以拆成:

部分 用途
[base] FHIR Server的Base URL
Patient 要搜尋的Resource類型
? 後方開始放入搜尋參數
name 搜尋參數名稱
王小明 搜尋值
= 連接參數名稱及搜尋值

這個Request的意思是:

請搜尋姓名符合王小明的Patient。


read和search有什麼不同?

read與search都可能使用GET,但兩者的使用情境不同。

Read

GET /Patient/patient-001

表示:

請讀取id為patient-001的Patient。

Search

GET /Patient?name=王小明

表示:

請搜尋姓名符合王小明的Patient。

兩者可以整理成:

比較項目 read search
是否需要Resource id 需要 不需要
查詢依據 id 姓名、生日或病歷號等條件
結果數量 一筆 零筆、一筆或多筆
成功Response Patient Bundle
找不到資料時 通常為404 通常回傳結果為0的Bundle

為什麼搜尋結果是Bundle?

搜尋可能找到:

  • 零筆資料
  • 一筆資料
  • 多筆資料

因此,FHIR使用Bundle包裝搜尋結果。

例如:

{
  "resourceType": "Bundle",
  "type": "searchset",
  "total": 1,
  "entry": [
    {
      "fullUrl": "https://hospital.example.org/fhir/Patient/patient-001",
      "resource": {
        "resourceType": "Patient",
        "id": "patient-001",
        "name": [
          {
            "text": "王小明"
          }
        ]
      }
    }
  ]
}

其中:

  • resourceTypeBundle
  • typesearchset
  • total表示符合條件的結果數量
  • entry放入搜尋結果
  • entry.resource是實際的Patient

即使只找到一筆Patient,搜尋Response仍然通常是Bundle,而不是直接回傳Patient。


常見Patient搜尋參數

FHIR R4的Patient Resource定義了多種搜尋參數,今天先介紹較常見的幾種。


使用_id搜尋

_id用來依照Resource的邏輯id搜尋。

GET /Patient?_id=patient-001

這和read看起來很相似,但Response不同。

Read

GET /Patient/patient-001

成功時直接回傳Patient。

Search by _id

GET /Patient?_id=patient-001

成功時回傳搜尋結果Bundle。

可以比較如下:

Request Response
GET /Patient/patient-001 Patient
GET /Patient?_id=patient-001 Bundle

使用identifier搜尋病歷號

Patient的identifier可能用來記錄病歷號。

假設Patient包含:

"identifier": [
  {
    "system": "https://hospital.example.org/mrn",
    "value": "MRN0001"
  }
]

搜尋概念可以寫成:

GET /Patient?identifier=MRN0001

不過,只提供value可能不夠精確,因為不同識別系統可能出現相同編號。

更明確的方式是同時提供systemvalue

GET /Patient?identifier=https://hospital.example.org/mrn|MRN0001

其中使用直線符號|分隔:

system|value

也就是:

https://hospital.example.org/mrn|MRN0001

表示:

搜尋病歷號系統為指定URI,而且病歷號為MRN0001的Patient。


使用name搜尋姓名

GET /Patient?name=王小明

name會針對Patient的姓名相關內容進行搜尋。

Patient姓名使用HumanName資料型別,可能包含:

  • text
  • family
  • given
  • prefix
  • suffix

不同FHIR Server對字串比對、語言及索引的實作可能不同,因此搜尋姓名時不一定只會找到完全相同的文字。


使用family搜尋姓氏

如果只知道姓氏,可以使用:

GET /Patient?family=王

family對應HumanName中的姓氏或家族名稱。

Patient資料可能包含:

"name": [
  {
    "family": "王",
    "given": [
      "小明"
    ]
  }
]

這時family=王可能找到這筆Patient。


使用given搜尋名字

GET /Patient?given=小明

given用來依照名字搜尋。

如果Patient為:

"name": [
  {
    "family": "王",
    "given": [
      "小明"
    ]
  }
]

那麼:

  • family是王
  • given是小明
  • name則可能搜尋整體姓名相關內容

不同文化對姓名的拆分方式可能不同,實際使用時需要依照Profile及在地實作規則處理。


使用birthdate搜尋出生日期

GET /Patient?birthdate=2000-01-01

birthdate屬於date類型的搜尋參數。

它對應Patient中的:

"birthDate": "2000-01-01"

日期格式通常使用:

YYYY-MM-DD

也就是:

年-月-日

如果日期格式不符合規範,Server可能無法理解搜尋條件。


使用gender搜尋性別

GET /Patient?gender=male

Patient的gender使用AdministrativeGender代碼,常見值包括:

  • male
  • female
  • other
  • unknown

因此,不應自行寫成:

gender=男

或:

gender=M

而要使用FHIR規範允許的代碼。


使用active搜尋有效紀錄

GET /Patient?active=true

active用來搜尋目前被視為有效使用中的Patient紀錄。

也可以搜尋:

GET /Patient?active=false

不過,active=false不代表病人已死亡,只表示該Patient紀錄目前不再被積極使用。


使用telecom搜尋聯絡方式

GET /Patient?telecom=0900-000-001

telecom可能搜尋:

  • 電話
  • 電子郵件
  • 傳真
  • 其他聯絡資料

如果只搜尋一串數字,可能找到使用該內容的不同ContactPoint。

實際系統也可能因電話格式不同而影響搜尋,例如:

0900000001
0900-000-001
+886-900-000-001

這些格式是否會被視為相同,取決於Server的標準化及搜尋實作。


使用address搜尋地址

GET /Patient?address=桃園

address可能針對Address中的多個欄位進行搜尋,例如:

  • text
  • line
  • city
  • district
  • state
  • postalCode
  • country

FHIR也定義較具體的地址搜尋參數,例如:

GET /Patient?address-city=中壢區
GET /Patient?address-postalcode=320

不同國家的地址結構差異很大,臺灣地址的實際搜尋方式仍需要參考TW Core IG及Server實作。


使用organization搜尋管理機構

Patient可以透過managingOrganization連結管理該病人紀錄的Organization。

例如:

"managingOrganization": {
  "reference": "Organization/hospital-001"
}

搜尋概念可以表示為:

GET /Patient?organization=Organization/hospital-001

或在特定情境中使用Resource id:

GET /Patient?organization=hospital-001

這類參數屬於Reference搜尋。實際接受的Reference表示方式需依FHIR規範及Server能力判斷。


搜尋參數也有資料型別

FHIR搜尋參數不是全部以相同方式比對。

常見搜尋參數型別包括:

類型 常見用途 Patient範例
string 文字 namefamilygiven
token 代碼或Identifier identifiergenderactive
date 日期 birthdate
reference Resource連結 organization
uri URI 部分標準識別資料
number 數值 其他Resource可能使用
quantity 數值加單位 Observation常見

不同型別具有不同的搜尋規則及修飾方式。

例如:

name=王小明

是string搜尋。

gender=male

是token搜尋。

birthdate=2000-01-01

是date搜尋。


同時使用多個搜尋條件

如果想同時使用姓名及出生日期,可以使用&連接參數:

GET /Patient?name=王小明&birthdate=2000-01-01

通常可以理解為:

姓名符合王小明,而且出生日期為2000年1月1日。

也就是AND關係。

再加入性別:

GET /Patient?name=王小明&birthdate=2000-01-01&gender=male

表示同時符合:

  • 姓名
  • 出生日期
  • 性別

多個條件可以縮小結果範圍,但仍不代表一定只會找到一位病人。


為什麼姓名加生日仍可能找到多筆?

可能有兩位病人同時:

  • 姓名相同
  • 出生日期相同
  • 性別相同

因此,即使加入多個條件,搜尋結果仍可能包含多筆Patient。

系統不能因為搜尋結果第一筆看起來很像,就直接認定它是正確病人。

病人比對可能還需要考慮:

  • Identifier
  • 聯絡方式
  • 地址
  • 管理機構
  • 其他經過授權的識別資料
  • 醫療機構的病人比對規則

錯誤連結病人資料可能影響病人安全,因此需要特別謹慎。


OR條件

FHIR搜尋可以使用逗號表示多個可能值,也就是OR概念。

例如:

GET /Patient?gender=male,female

可以理解為:

搜尋gender為male或female的Patient。

概念上:

male OR female

不過,並非每一個搜尋參數或FHIR Server都一定支援所有多值搜尋形式,因此仍要查看Server的CapabilityStatement及實作說明。


重複參數與AND條件

FHIR搜尋也可能重複使用相同參數,例如:

GET /Patient?address=桃園&address=中壢

概念上希望搜尋地址同時符合桃園及中壢的Patient。

也就是:

桃園 AND 中壢

但是,各搜尋參數對重複值的支援及比對方式可能有所限制。正式使用時需要確認FHIR規範及Server實作,不能假設所有參數都具有完全相同的AND或OR行為。


String搜尋修飾詞

FHIR允許部分搜尋參數使用Modifier,進一步說明比對方式。

Modifier會放在搜尋參數名稱後面,例如:

name:exact

:exact

GET /Patient?name:exact=王小明

:exact表示進行較精確的字串比對。

與一般string搜尋相比,它通常會更重視完整內容、大小寫及空白等差異。


:contains

GET /Patient?name:contains=小明

:contains表示搜尋字串中包含指定內容的資料。

例如,「王小明」可能包含「小明」。

不過,Server是否支援:contains,仍應查看CapabilityStatement及實作規則。


Date搜尋前綴

date搜尋可以搭配前綴,表示日期之間的比較關係。

常見前綴包括:

前綴 意義
eq 等於
ne 不等於
gt 大於或晚於
lt 小於或早於
ge 大於等於
le 小於等於
sa 開始於指定時間之後
eb 結束於指定時間之前
ap 約等於

例如:

GET /Patient?birthdate=ge2000-01-01

可以理解為:

搜尋出生日期大於或等於2000年1月1日的Patient。

GET /Patient?birthdate=lt2010-01-01

表示:

搜尋出生日期早於2010年1月1日的Patient。

前綴應直接放在日期值前面,中間沒有空格。


Token搜尋中的system與code

gender、identifier等欄位常使用token搜尋。

如果只提供Value:

GET /Patient?identifier=MRN0001

表示搜尋識別碼值為MRN0001的Patient。

如果同時指定System:

GET /Patient?identifier=https://hospital.example.org/mrn|MRN0001

表示同時比對:

  • Identifier System
  • Identifier Value

代碼搜尋也可能使用相似形式:

system|code

這通常比只提供Value或Code更加明確。


控制搜尋結果數量

FHIR提供_count參數,用來要求每一頁回傳的結果數量。

例如:

GET /Patient?_count=10

表示Client希望每一頁最多回傳10筆Patient。

需要注意:

  • _count不是限制所有符合條件的總數。
  • 搜尋總結果可能仍然大於10。
  • Server可能依照自身政策調整實際頁面大小。
  • 後續結果可能透過Bundle的next連結取得。

搜尋結果為零時

如果沒有找到符合條件的Patient,FHIR Server通常不會因為「沒有搜尋結果」就回傳404。

Response仍可能是:

200 OK

Body則是結果為0的Bundle:

{
  "resourceType": "Bundle",
  "type": "searchset",
  "total": 0
}

可以比較:

情境 常見Response
read指定id,但Resource不存在 404 Not Found
search沒有找到符合條件的資料 200 OK及空的搜尋Bundle

這是read與search的重要差異。


搜尋結果的Bundle

一份搜尋Bundle可能包含:

{
  "resourceType": "Bundle",
  "type": "searchset",
  "total": 2,
  "link": [
    {
      "relation": "self",
      "url": "https://hospital.example.org/fhir/Patient?name=王小明"
    }
  ],
  "entry": [
    {
      "fullUrl": "https://hospital.example.org/fhir/Patient/patient-001",
      "search": {
        "mode": "match"
      },
      "resource": {
        "resourceType": "Patient",
        "id": "patient-001"
      }
    },
    {
      "fullUrl": "https://hospital.example.org/fhir/Patient/patient-002",
      "search": {
        "mode": "match"
      },
      "resource": {
        "resourceType": "Patient",
        "id": "patient-002"
      }
    }
  ]
}

其中:

  • total為2,表示有兩筆符合條件。
  • entry包含兩筆搜尋結果。
  • fullUrl是Resource的完整位置。
  • search.modematch,表示該Resource符合搜尋條件。
  • entry.resource是實際的Patient。

Bundle會在下一篇文章中進一步介紹。


Server不一定支援所有搜尋參數

FHIR R4規範定義了許多Patient搜尋參數,但個別FHIR Server不一定全部支援。

Server可能只支援:

  • _id
  • identifier
  • name
  • birthdate

卻不支援:

  • address
  • telecom
  • organization

因此,需要查看CapabilityStatement中的:

rest → resource → searchParam

確認Patient實際支援哪些搜尋參數。

如果使用不支援的參數,Server可能:

  • 拒絕Request
  • 回傳OperationOutcome
  • 忽略未知參數並提供警告
  • 依照自身實作方式處理

Client不能假設所有Server對未知搜尋參數的反應完全相同。


搜尋姓名不等於完成病人身分確認

搜尋Patient最大的風險之一,是將搜尋結果誤認為正確病人。

例如,搜尋:

GET /Patient?name=王小明

可能找到多位同名病人。

只比對姓名可能受到以下因素影響:

  • 同名同姓
  • 姓名輸入錯誤
  • 曾用名
  • 姓名順序不同
  • 簡體與繁體差異
  • 英文拼音方式不同
  • Resource資料不完整
  • 不同醫院使用不同病歷號

因此,Patient搜尋只是「找出可能符合的候選資料」,不一定能直接完成身分確認。

醫療機構仍需要依照病人比對政策,使用足夠且合法的資訊確認病人身分。


搜尋涉及個資與權限

Patient搜尋可能接觸:

  • 姓名
  • 出生日期
  • 病歷號
  • 電話
  • 地址
  • 醫療機構關係

這些都屬於敏感資料。

正式FHIR Server通常需要控制:

  • 誰可以搜尋Patient
  • 可以使用哪些搜尋條件
  • 可以看到哪些結果
  • 是否限制結果數量
  • 是否記錄查詢行為
  • 是否遮蔽部分欄位
  • 是否只允許搜尋具有照護關係的病人

如果允許未授權使用者反覆搜尋姓名及生日,可能造成病人資料外洩。

所以FHIR定義搜尋方法,不代表所有人都具有搜尋權限。


Patient搜尋概念整理

需求 搜尋概念
依FHIR Resource id Patient?_id=patient-001
依病歷號 Patient?identifier=system|value
依姓名 Patient?name=王小明
依姓氏 Patient?family=王
依名字 Patient?given=小明
依出生日期 Patient?birthdate=2000-01-01
依性別 Patient?gender=male
依有效狀態 Patient?active=true
依聯絡方式 Patient?telecom=...
依地址 Patient?address=桃園
依管理機構 Patient?organization=...
多條件AND 使用&連接
多值OR 使用逗號分隔
精確字串 使用:exact
包含字串 使用:contains
日期比較 使用gelt等前綴

實際可用參數及修飾詞,仍然要以FHIR Server的CapabilityStatement及實作說明為準。


今日小結

今天認識了FHIR Patient的search互動。

當Client不知道Patient id,但知道姓名、出生日期、病歷號或其他條件時,可以透過搜尋參數尋找可能符合的Patient。

常見參數包括:

  • _id
  • identifier
  • name
  • family
  • given
  • birthdate
  • gender
  • active
  • telecom
  • address
  • organization

搜尋結果通常使用typesearchset的Bundle包裝。即使沒有找到資料,也可能回傳200 OK及結果為0的Bundle,而不是404。

今天最重要的觀念是:

搜尋找到的Patient只是候選結果,不代表已經完成病人身分確認。

正式醫療系統需要使用適當的識別資料、權限及病人比對規則,避免將資料連結到錯誤病人。

下一篇將專門介紹FHIR Bundle,進一步拆解totallinkentryfullUrl及搜尋分頁等內容。

明日預告

Day 19|搜尋結果為什麼變成Bundle?

參考資料

  1. HL7 FHIR R4:Search
    https://hl7.org/fhir/R4/search.html

  2. HL7 FHIR R4:Patient Search Parameters
    https://hl7.org/fhir/R4/patient.html#search

  3. HL7 FHIR R4:SearchParameter
    https://hl7.org/fhir/R4/searchparameter.html

  4. HL7 FHIR R4:Bundle
    https://hl7.org/fhir/R4/bundle.html

  5. HL7 FHIR R4:Patient Matching
    https://hl7.org/fhir/R4/patient.html#match


上一篇
Day 17|第一次用API讀取Patient資料
下一篇
Day 19|搜尋結果為什麼變成Bundle?
系列文
《醫資生的 FHIR 30日入門:用 Postman 讀懂醫療資料交換》30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言